package grdnlog.dao;

import grdnlog.bean.AppEngineConnectionData;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionKey;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.social.connect.UsersConnectionRepository;

public class GoogleAppEngineUsersConnectionRepository implements UsersConnectionRepository {

	private final ConnectionFactoryLocator connectionFactoryLocator;

	private final TextEncryptor textEncryptor;

	private ConnectionSignUp connectionSignUp;

	public GoogleAppEngineUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator, TextEncryptor textEncryptor) {

		this.connectionFactoryLocator = connectionFactoryLocator;
		this.textEncryptor = textEncryptor;
	}

	/**
	 * The command to execute to create a new local user profile in the event no user id could be mapped to a connection.
	 * Allows for implicitly creating a user profile from connection data during a provider sign-in attempt.
	 * Defaults to null, indicating explicit sign-up will be required to complete the provider sign-in attempt.
	 * @see #findUserIdWithConnection(Connection)
	 */
	public void setConnectionSignUp(ConnectionSignUp connectionSignUp) {
		this.connectionSignUp = connectionSignUp;
	}
	

	
	public String findUserIdWithConnection(Connection<?> connection) {
		try {
			ConnectionKey key = connection.getKey();
			
			EntityManager em = this.getEntityManager();
			
			Query query = em.createQuery("SELECT o FROM grdnlog.bean.AppEngineConnectionData WHERE o.providerId = ?1 AND o.providerUserId = ?2");
			query.setParameter(1, key.getProviderId());
			query.setParameter(2, key.getProviderUserId());
			
			AppEngineConnectionData data = (AppEngineConnectionData) query.getSingleResult();
			
			return data.getUserId();
		} catch (NoResultException e) {
			if (connectionSignUp != null) {
				String newUserId = connectionSignUp.execute(connection);
				createConnectionRepository(newUserId).addConnection(connection);
				return newUserId;
			}
			return null;
		} catch (NonUniqueResultException e) {
			return null;
		}
	}

	public Set<String> findUserIdsConnectedTo(String providerId, Set<String> providerUserIds) {
		final Set<String> localUserIds = new HashSet<String>();
		
		EntityManager em = this.getEntityManager();
		
		Query query = em.createQuery("SELECT o FROM grdnlog.bean.AppEngineConnectionData WHERE o.providerId = :providerId AND o.providerUserId in (:providerUserIds)");
		query.setParameter("providerId", providerId);
		query.setParameter("providerUserIds", providerUserIds);
		
		List<AppEngineConnectionData> resultList = query.getResultList();
		
		for(AppEngineConnectionData data : resultList) {
			localUserIds.add(data.getUserId());
		}
		
		return localUserIds;
	}

	public ConnectionRepository createConnectionRepository(String userId) {
		if (userId == null) {
			throw new IllegalArgumentException("userId cannot be null");
		}
		return new GoogleAppEngineConnectionRepository(userId, connectionFactoryLocator, textEncryptor);
	}
	
	private EntityManager entityManager;

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	public EntityManager getEntityManager() {
		return entityManager;
	}
	
}
